/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.statemachine.figure;


import java.util.Iterator;

import org.eclipse.draw2d.FreeformLayer;
import org.eclipse.draw2d.FreeformLayout;
import org.eclipse.draw2d.FreeformViewport;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.LineBorder;
import org.eclipse.draw2d.ScrollPane;
import org.eclipse.draw2d.StackLayout;
import org.eclipse.draw2d.ToolbarLayout;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.uml2.uml.AnyReceiveEvent;
import org.eclipse.uml2.uml.CallEvent;
import org.eclipse.uml2.uml.ChangeEvent;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Event;
import org.eclipse.uml2.uml.LiteralString;
import org.eclipse.uml2.uml.SignalEvent;
import org.eclipse.uml2.uml.TimeEvent;
import org.eclipse.uml2.uml.Trigger;

import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.figures.ElementFigure;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;

/**
 * Figure of a State.
 * 
 * @author David Servat
 * @since 26 apr. 2007
 */
public class StateFigure extends NodeFigure{

	/**
	 * The debug channel.
	 */
	private int debugChannel = 501;

	/**
	 * 
	 */
	public static Color classColor = PapyrusPlugin.colorManager.get(new RGB(227,221,239));

	/**
	 * 
	 */
	public static Color classBorderColor= PapyrusPlugin.colorManager.get(new RGB(166,157,183));

	/**
	 * Header figure.
	 */
	protected StateHeaderFigure headerFigure = null;

	/**
	 * Content figure: for entry, do, etc.
	 */
	protected StateInformationFigure informationFigure = null;

	/**
	 * Content figure for composite states: regions, etc.
	 */
	protected StateCompositeFigure compositeFigure = null;

	/**
	 * Flag to toggle show for entry.
	 */
	protected boolean showEntry = false;

	/**
	 * Flag to toggle show for doActivity.
	 */
	protected boolean showDoActivity = false;

	/**
	 * Flag to toggle show for exit.
	 */
	protected boolean showExit = false;

	/**
	 * Flag to toggle show for deferrable triggers.
	 */
	protected boolean showDefferableTriggers = false;

	/**
	 * Number of deferrable triggers to show.
	 */
	protected int numberOfDefferableTriggers = 0;

	/**
	 * Flag to show composite content.
	 */
	protected boolean showComposite = false;

	/**
	 * Text for entry.
	 */
	protected String entryText = " /entry";

	/**
	 * Text for exit.
	 */
	protected String exitText = " /exit";

	/**
	 * Text for doActivity.
	 */
	protected String doActivityText = " /do";

	/**
	 * Text for defferableTriggers.
	 */
	protected String deferrableTriggersText = "";

	/**
	 * Initial information figure height.
	 */
	protected int initialInformationFigureHeight = 20;

	/**
	 * Current information figure height.
	 */
	protected int currentInformationFigureHeight;

	/**
	 * Old information figure height.
	 */
	protected int oldInformationFigureHeight;
	
	/**
	 * Header height.
	 */
	protected int headerHeight;

	/**
	 * Header figure
	 */
	class StateHeaderFigure extends ElementFigure{

		/**
		 * The constructor.
		 */
		public StateHeaderFigure(){
			Debug.debug(this,"  Call StateHeaderFigure()", debugChannel);

			ToolbarLayout layout = new ToolbarLayout();
			setLayoutManager(layout);
	
			this.borderColor = null;
			setOpaque(false);
		}

		/**
		 * Overriden to center the name label.
		 * 
		 * @param rect new Rectangle bounds.
		 * 
		 */
		@Override
		public void setBounds(Rectangle rect){
			super.setBounds(rect);
			nameLabel.setBounds(rect);

			Debug.debug(this,"Bounds are : " +getBounds(), debugChannel);
		}

		/**
		 * Overriden to get rid of the border.
		 * 
		 * @param border 
		 */
		@Override
		protected void paintBorder(Graphics graphics){
		}
	}


	/**
	 * Additional information of State: entry, exit, doActivity, etc.
	 * 
	 */
	class StateInformationFigure extends ElementFigure{

		/**
		 * The constructor.
		 */
		public StateInformationFigure(){
			Debug.debug(this,"Call StateInformationFigure()", debugChannel);

			ToolbarLayout layout = new ToolbarLayout();
			setLayoutManager(layout);

			this.borderColor = null;
			setOpaque(false);
		}

		/**
		 * Overriden to center the name label.
		 * 
		 * @param rect new Rectangle.
		 * 
		 */

		@Override
		public void setBounds(Rectangle rect){
			super.setBounds(rect);
			nameLabel.setBounds(rect);

			Debug.debug(this,"Bounds are : " +getBounds(), debugChannel);
		}

		/**
		 * Overriden to get rid of the border.
		 * 
		 * @param border 
		 */
		@Override
		protected void paintBorder(Graphics graphics){
		}
	}


	/**
	 * Composite content of State.
	 */
	class StateCompositeFigure extends ElementFigure{

		/**
		 * A pane.
		 */
		private IFigure pane;

		/**
		 * A scroll pane.
		 */
		protected ScrollPane scrollpane;

		/**
		 * The constructor.
		 */
		public StateCompositeFigure(){
			Debug.debug(this,"Call StateCompositeFigure", debugChannel);

			//set content layout
			setLayoutManager(new StackLayout());
			//add pane for content
			pane = new FreeformLayer();
			pane.setLayoutManager(new FreeformLayout());

			//add scrollpane
			ScrollPane scrollpane = new ScrollPane();
			scrollpane.setViewport(new FreeformViewport());
			scrollpane.setContents(pane);
			this.add(pane);

			//set colors
			setBackgroundColor(headerFigure.getBackgroundColor());
			setBorder(new LineBorder(headerFigure.getBorderColor()));
			setOpaque(false);
		}

		/**
		 * Returns the content pane.
		 * 
		 * @return the corresponding IFigure.
		 */
		@Override
		public IFigure getContentPane(){
			return pane;
		}

		/**
		 * Overriden to display debug information.
		 * 
		 * @param rect new Rectangle dimensions.
		 */		
		@Override
		public void setBounds(Rectangle rect){
			super.setBounds(rect);
			Debug.debug(this,"Bounds are : " +getBounds(), debugChannel);
		}

		/**
		 * Overriden to get rid of the border.
		 * 
		 * @param border 
		 */
		@Override
		protected void paintBorder(Graphics graphics){
		}
	}


	/**
	 * The main State figure constructor.
	 */
	public StateFigure(){
		ToolbarLayout layout = new ToolbarLayout();
		setLayoutManager(layout);

		headerFigure = new StateHeaderFigure();
		add(headerFigure);

		informationFigure = new StateInformationFigure();
		add(informationFigure);

		compositeFigure = new StateCompositeFigure();
		add(compositeFigure);

		setOpaque(false);
	}

	/**
	 * @see com.cea.papyrus.common.figures.AbstractElementFigure#setBackgroundColor(org.eclipse.swt.graphics.Color)
	 */
	@Override
	public void setBackgroundColor(Color backgroundColor){
		super.setBackgroundColor(backgroundColor);
		headerFigure.setBackgroundColor(backgroundColor);
	}

	/** 
	 * @see com.cea.papyrus.common.figures.AbstractElementFigure#setFontColor(org.eclipse.swt.graphics.Color)
	 */
	@Override
	public void setFontColor(Color color){
		this.fontColor=color;
		headerFigure.setFontColor(color);
	}

	/**
	 * @see com.cea.papyrus.common.figures.AbstractElementFigure#setForeGroundColor(org.eclipse.swt.graphics.Color)
	 */
	@Override
	public void setForeGroundColor(Color color){
		super.setForeGroundColor(color);
		informationFigure.setBackgroundColor(color);
		compositeFigure.setBackgroundColor(color);
	}

	/**
	 * Main method to update dimensions.
	 * 
	 * @param rect the new Rectangle dimensions.
	 */
	@Override
	public void setBounds(Rectangle rect){
		super.setBounds(new Rectangle(rect));

		if(showComposite){
			//information
			informationFigure.setBounds(new Rectangle(0, headerHeight, rect.width, currentInformationFigureHeight));
			//header
			headerFigure.setBounds(new Rectangle(0, 0, rect.width, headerHeight));
			//compositeContent
			compositeFigure.setBounds(new Rectangle(0, headerHeight+currentInformationFigureHeight, rect.width, rect.height-headerHeight-currentInformationFigureHeight));
		}
		else{
			//information
			informationFigure.setBounds(new Rectangle(0, rect.height-currentInformationFigureHeight, rect.width, currentInformationFigureHeight));
			//header
			headerFigure.setBounds(new Rectangle(0, 0, rect.width, rect.height-currentInformationFigureHeight));
			//compositeContent
			compositeFigure.setBounds(new Rectangle(0, headerHeight+currentInformationFigureHeight, rect.width, 0));
		}
		corner = new Dimension(this.getBounds().getSize());
		corner.height /= 10;
		if(corner.height > getHeadHeight())
			corner.height = getHeadHeight();
		corner.width = corner.height;

		Debug.debug(this,"Bounds are : " +getBounds(), debugChannel);
	}

	/**
	 * @see com.cea.papyrus.common.figures.AbstractElementFigure#setBorderColor(org.eclipse.swt.graphics.Color)
	 */
	@Override
	public void setBorderColor(Color borderColor){
		this.borderColor = borderColor;
	}

	/**
	 * Returns the header.
	 * 
	 * @return IFigure the header.
	 */
	public StateHeaderFigure getHeader(){
		return headerFigure;
	}

	/**
	 * Returns the content pane.
	 * 
	 * @return corresponding IFigure.
	 */
	public IFigure getContentsPane(){
		return compositeFigure.getContentPane();
	}

	/**
	 * Overriden flag.
	 * 
	 * @return 
	 */
	@Override
	protected boolean useLocalCoordinates(){
		return true;
	}

	/**
	 * Utility methods.
	 */
	public int getHeadWidth(){
		return headerFigure.getSize().width;
	}

	/**
	 * Utility methods.
	 */
	public int getHeadHeight(){ 
		return headerFigure.getSize().height;
	}

	/**
	 * Utility methods.
	 */
	public int getCurrentHeadHeight(){
		return headerHeight;
	}
	/**
	 * Utility methods.
	 */
	public int getInformationHeight(){
		return informationFigure.getSize().height;
	}

	/**
	 * Overrides method from AbstractElementFigure.
	 * 
	 * @return Returns the nameLabel.
	 */
	@Override
	public Label getNameLabel(){
		return headerFigure.getNameLabel();
	}

	/**
	 * Overrides method from AbstractElementFigure.
	 * 
	 * @return Returns the qualifiedNameLabel.
	 */
	@Override
	public Label getQualifiedNameLabel(){
		return headerFigure.getQualifiedNameLabel();
	}

	/**
	 * Overrides method from AbstractElementFigure.
	 * 
	 * @return Returns the stereotype Label.
	 */
	@Override
	public Label getStereotypesLabel(){
		return headerFigure.getStereotypesLabel();
	}

	/**
	 * Sets the name.
	 * 
	 * @param name 
	 */
	@Override
	public void setName(String name){
		headerFigure.setName(name);
	}

	/**
	 * Sets qualified name.
	 * 
	 * @param packageName 
	 */
	@Override
	public void setQualifiedName(String name){
		headerFigure.setQualifiedName(name);
	}

	/**
	 * Set the stereotype value.
	 * If param is null, remove the label.
	 * 
	 * @param stereotypes 
	 */
	@Override
	public void setStereotypes(String stereotypes){
		headerFigure.setStereotypes(stereotypes);
	}

	/**
	 * Refresh presentation of current figure
	 * with presentation specified in a GraphNode.
	 * 
	 * @param presentation 
	 * @param selection 
	 */
	@Override
	public void refreshPresentation(String presentation, Element selection, GraphElement ge){
		headerFigure.refreshPresentation(presentation, selection, ge);
	}

	/**
	 * Refresh list of stereotypes of current EditPart if required
	 * either with or without the first stereotype.
	 * 
	 * @param presentation 
	 * @param hasIcon 
	 * @param hasShape 
	 * @param stereotypes 
	 */
	@Override
	public void refreshStereotypes(String stereotypes, String presentation, boolean hasIcon, boolean hasShape){
		headerFigure.refreshStereotypes(stereotypes, presentation, hasIcon, hasShape);
	}

	/**
	 * Gets show entry flag.
	 */
	public boolean getShowEntry(){
		return showEntry;
	}

	/**
	 * Gets show doActivity flag.
	 */
	public boolean getShowDoActivity(){
		return showDoActivity;
	}

	/**
	 * Gets show exit flag.
	 */
	public boolean getShowExit(){
		return showExit;
	}

	/**
	 * Gets show deferrable triggers flag.
	 */
	public boolean getShowDeferrableTriggers(){
		return showDefferableTriggers;
	}

	/**
	 * Gets number of deferrable triggers to show.
	 */
	public int getNumberOfDeferrableTriggers(){
		return numberOfDefferableTriggers;
	}

	/**
	 * Sets show entry flag.
	 */
	public void setShowEntry(boolean show){
		showEntry = show;
	}

	/**
	 * Sets show doActivity flag.
	 */
	public void setShowDoActivity(boolean show){
		showDoActivity = show;
	}

	/**
	 * Sets show exit flag.
	 */
	public void setShowExit(boolean show){
		showExit = show;
	}

	/**
	 * Sets show deferrable triggers flag.
	 */
	public void setShowDeferrableTriggers(boolean show){
		showDefferableTriggers = show;
	}

	/**
	 * Sets number of deferrable triggers to show.
	 */
	public void setNumberOfDeferrableTriggers(int n){
		numberOfDefferableTriggers = n;
	}

	/**
	 * Sets show composite.
	 */
	public void setShowComposite(boolean show){
		showComposite = show;
	}

	/** 
	 * Utility method.
	 */
	public boolean showInformation(){
		return showEntry|showExit|showDoActivity|showDefferableTriggers;
	}

	/**
	 * Sets the entry text.
	 * @param name String.
	 */
	public void setEntryText(String name){
		entryText = name+" /entry\n";
	}

	/**
	 * Sets the exit text.
	 * @param name String.
	 */
	public void setExitText(String name){
		exitText = name+" /exit\n";
	}

	/**
	 * Sets the deferrableTriggers text.
	 * @param name String.
	 */
	public void setDeferrableTriggersText(Iterator it){
		deferrableTriggersText = "";
		while(it.hasNext()){
			Trigger trigger = (Trigger)it.next();
			Event event = trigger.getEvent();
			if(event != null){
				if(event instanceof CallEvent)
					deferrableTriggersText += com.cea.papyrus.umlutils.Operation.getSignature(((CallEvent)event).getOperation(), false);
				else if(event instanceof SignalEvent)
					deferrableTriggersText += ((SignalEvent)event).getSignal().getName();
				else if(event instanceof AnyReceiveEvent)
					deferrableTriggersText += "all";
				else if(event instanceof ChangeEvent){
					ChangeEvent changeEvent = (ChangeEvent)event;
					if((changeEvent.getChangeExpression() != null) && (changeEvent.getChangeExpression() instanceof LiteralString))
						deferrableTriggersText += "when "+changeEvent.getChangeExpression().stringValue();
					else
						deferrableTriggersText += "when "+changeEvent.getName();
				}		
				else if(event instanceof TimeEvent){
					TimeEvent timeEvent = (TimeEvent)event;
					if((timeEvent.getWhen() != null) && (timeEvent.getWhen() instanceof LiteralString))
						deferrableTriggersText += timeEvent.getWhen().stringValue();
					else
						deferrableTriggersText += timeEvent.getName();
				}		
			}
			else{
				deferrableTriggersText += trigger.getName();
			}

			deferrableTriggersText += " /defer\n";
		}
	}

	/**
	 * Sets the doActivity text.
	 * @param name String.
	 */
	public void setDoActivityText(String name){
		doActivityText = name+" /do\n";
	}

	/**
	 * Refresh figure.
	 *
	 */
	public void refreshFigure(){
		//header
		headerHeight = initialInformationFigureHeight;
		if(getStereotypesLabel() != null)
			headerHeight += initialInformationFigureHeight;
		if(getQualifiedNameLabel() != null)
			headerHeight += initialInformationFigureHeight;
		
		//information
		String fullText = "";
		oldInformationFigureHeight = currentInformationFigureHeight;
		currentInformationFigureHeight = 0;
		if(showEntry){
			fullText += entryText;
			currentInformationFigureHeight += initialInformationFigureHeight;
		}
		if(showDoActivity){
			fullText += doActivityText;
			currentInformationFigureHeight += initialInformationFigureHeight;
		}
		if(showExit){
			fullText += exitText;
			currentInformationFigureHeight += initialInformationFigureHeight;
		}
		if(showDefferableTriggers){
			fullText += deferrableTriggersText;
			currentInformationFigureHeight += numberOfDefferableTriggers*initialInformationFigureHeight;
		}
		if(fullText.length()>1)
			fullText = fullText.substring(0, fullText.length()-1);
		informationFigure.setName(fullText);
	}

	/**
	 * Gets the old information figure height.
	 */
	public int getOldInformationFigureHeight(){
		return oldInformationFigureHeight;
	}

	/**
	 * Gets the current information figure height.
	 */
	public int getCurrentInformationFigureHeight(){
		return currentInformationFigureHeight;
	}

	/**
	 * Main refresh painting method.
	 * 
	 * @param graphics 
	 */
	@Override
	public void paintFigure(Graphics graphics){
		graphics.setForegroundColor(getBorderColor());

		super.paintFigure(graphics);

		Rectangle rect = getBounds();
		setBounds(rect);
		graphics.setLineStyle(SWT.LINE_SOLID);
		int height = getHeadHeight();
		if(showInformation()){
			graphics.drawLine(rect.x, rect.y+height, rect.x+rect.width, rect.y+height);
			height += currentInformationFigureHeight;
		}
		if(showComposite){
			graphics.drawLine(rect.x, rect.y+height, rect.x+rect.width, rect.y+height);
		}
	}

}
